/**
 * ANNCSR (2010-2011)
 */
package net.abbp.mtr.control;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JPanel;

import net.abbp.core.util.DateUtils;
import net.abbp.core.util.StringUtils;
import net.abbp.dao.DaoFactory;
import net.abbp.dao.IDao;
import net.abbp.dao.condition.Cnds;
import net.abbp.dao.condition.Operator;
import net.abbp.mtr.config.ChannelManager;
import net.abbp.mtr.data.Record;
import net.abbp.mtr.event.DataArriveEvent;
import net.abbp.mtr.event.IDataArriveListener;

/**
 * 
 * 图形化监控,绘制温度测量线，当前每通道支持600个点的绘制
 * 
 * @version 1.0 2010-12-19
 * @author lexloo(lexloo@gmail.com)
 * @since MTR 1.0
 */
@SuppressWarnings("serial")
public class GraphMonitor extends JPanel implements IDataArriveListener {
    // 最大测量显示温度
    private final int T_MAX = 25;
    // 最小测量显示温度
    private final int T_MIN = -25;
    // 温度显示区间
    private final int T_SPAN = T_MAX - T_MIN;

    private final int PADDING_TOP = 20;
    private final int PADDING_BOTTOM = 20;

    private JPanel monitorPane;
    private JPanel yPane;
    private JPanel xPane;
    private JPanel cornor;
    private IDao dao = DaoFactory.INSTANCE.newDao();
    // 起始日期
    private Calendar startDate;
    // 临时日期，用于比较
    private Calendar compareDate = Calendar.getInstance();

    // 通道数据
    private Map<String, List<Record>> channelDatas;

    public GraphMonitor() {
        channelDatas = new HashMap<String, List<Record>>();

        this.setStartDate(Calendar.getInstance());

        this.initComponents();
    }

    /**
	 * 
	 */
    private void initChannelData() {
        channelDatas.clear();

        Set<String> channels = ChannelManager.INSTANCE.getConfigs().keySet();

        for (String channel : channels) {
            List<Record> recordList = dao.query(Record.class, Cnds.and("time", Operator.GreaterThanOrEqual, this
                    .getStartDate()));
            channelDatas.put(channel, recordList);
        }
    }

    /**
     * 时间转换为X坐标
     * 
     * @param pixelPerSeconds
     *            每秒对应的像素
     * @param date
     *            时间
     * @return X坐标
     */
    public int transX(double pixelPerSeconds, Date date) {
        return (int) ((date.getTime() - this.startDate.getTimeInMillis()) * pixelPerSeconds / 1000);
    }

    /**
     * 根据温度转化为Y坐标
     * 
     * @param yMax
     *            最低温度对应的坐标
     * @param ySpan
     *            Y坐标跨度
     * @param temp
     *            温度值
     * @return Y坐标
     */
    public int transY(int yMax, int ySpan, double temp) {
        int y = (int) (yMax - ySpan * (temp - T_MIN) / T_SPAN);

        return y;
    }

    /**
     * 初始化控件
     */
    private void initComponents() {
        monitorPane = new TMMonitorPane();
        yPane = new YAxisPane();
        xPane = new XAxisPane();
        cornor = new CornorPane();

        this.setLayout(new BorderLayout());
        JPanel topPane = new JPanel();
        this.add(topPane, BorderLayout.CENTER);

        topPane.setLayout(new BorderLayout());
        topPane.add(monitorPane, BorderLayout.CENTER);
        topPane.add(yPane, BorderLayout.WEST);

        JPanel bottomPane = new JPanel();
        this.add(bottomPane, BorderLayout.SOUTH);

        bottomPane.setLayout(new BorderLayout());
        bottomPane.add(xPane, BorderLayout.CENTER);
        bottomPane.add(cornor, BorderLayout.WEST);
    }

    public void setStartDate(Calendar startDate) {
        this.startDate = DateUtils.getZeroCalendar(startDate.getTime());
        initChannelData();
    }

    public Calendar getStartDate() {
        return startDate;
    }

    /**
     * 监控面板
     */
    private class TMMonitorPane extends JPanel {
        private TMMonitorPane() {
            this.initComponents();
        }

        /**
         * 初始化
         */
        private void initComponents() {
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2 = (Graphics2D) g;
            g2.setBackground(Color.BLACK);

            int h = this.getHeight();
            int w = this.getWidth();
            Rectangle client = new Rectangle(0, 0, w, h);
            g2.fill(client);

            int yMax = h - PADDING_BOTTOM;
            int ySpan = yMax - PADDING_TOP;

            // 绘制纵轴(绘制一天的数据)
            int maxSpan = w / 24;
            int minSpan = maxSpan / 6;
            for (int i = 0; i <= 24; i++) {
                int x = maxSpan * i;
                g2.setColor(Color.WHITE);
                g2.drawLine(x, PADDING_TOP, x, yMax);

                if (i == 0) {
                    g2.drawString(DateUtils.formatDate(startDate.getTime()), x, yMax + 20);
                }
                g2.drawString(String.valueOf(i), x - 2, yMax + 10);

                if (i != 24) {
                    g2.setColor(Color.GRAY);
                    for (int j = 1; j < 6; j++) {
                        x += minSpan;
                        g2.drawLine(x, PADDING_TOP, x, yMax);
                    }
                }
            }

            g2.setColor(Color.WHITE);

            // 绘制横抽
            for (int i = T_MIN; i <= T_MAX; i += 5) {
                int y = transY(yMax, ySpan, i);
                g2.drawLine(0, y, w, y);
            }

            g2.setColor(Color.RED);

            double pixelPerSeconds = ((double) w) / (24 * 60 * 60);

            // 绘制各通道的数据
            for (List<Record> channelData : channelDatas.values()) {
                int size = channelData.size();
                int x[] = new int[size];
                int y[] = new int[size];

                for (int i = 0; i < size; i++) {
                    Record r = channelData.get(i);
                    x[i] = transX(pixelPerSeconds, r.getTime());
                    y[i] = transY(yMax, ySpan, r.getValue());
                }

                g2.drawPolyline(x, y, size);
            }
        }
    }

    /**
     * 纵坐标面板
     */
    private class YAxisPane extends JPanel {
        // 右边距
        private final int PADDING_RIGHT = 8;
        // 坐标轴长度
        private final int AXIS_LENGHT = 10;

        private YAxisPane() {
            this.initComponents();
        }

        /**
         * 初始化
         */
        private void initComponents() {
            this.setPreferredSize(new Dimension(80, 10));
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2 = (Graphics2D) g;

            g2.setColor(Color.BLUE);

            int h = this.getHeight();
            int w = this.getWidth();
            int pX = w - PADDING_RIGHT;
            Line2D yAxis = new Line2D.Double(pX, PADDING_TOP, pX, h - PADDING_BOTTOM);
            g2.draw(yAxis);

            int pSX = pX - AXIS_LENGHT;
            int pSSX = pX - 30;
            int yMax = h - PADDING_BOTTOM;
            int ySpan = yMax - PADDING_TOP;
            for (int i = T_MIN; i <= T_MAX; i += 5) {
                int y = transY(yMax, ySpan, i);
                g2.drawLine(pSX, y, pX, y);
                g2.drawString(StringUtils.padLeft(String.valueOf(i), ' ', 3), pSSX, y + 4);
            }
        }
    }

    /**
     * 横坐标面板
     */
    private class XAxisPane extends JPanel {
        private XAxisPane() {
            this.initComponents();
        }

        /**
         * 初始化
         */
        private void initComponents() {
        }
    }

    /**
     * 角落
     */
    private class CornorPane extends JPanel {
        private CornorPane() {
            this.initComponents();
        }

        /**
         * 初始化
         */
        private void initComponents() {
            this.setPreferredSize(new Dimension(80, 32));
        }
    }

    @Override
    public void dataArrive(DataArriveEvent evt) {
        this.compareDate.setTime(evt.getData().getTime());
        if (this.compareDate.get(Calendar.DATE) > this.startDate.get(Calendar.DATE)) {
            GraphMonitor.this.setStartDate(compareDate);
        }

        List<Record> chnList = channelDatas.get(evt.getChannel());
        if (chnList != null) {
            chnList.add(evt.getData());
            this.monitorPane.repaint();
        }
    }
}
